﻿using System.Linq;
using System.IO;
using System.Windows;
using System.Windows.Forms;
using System.IO.Compression;
using System.IO.Packaging;
using System;
using TestClient.WcfCGServices;
using System.Diagnostics;
using System.Threading.Tasks;
using System.ServiceModel;
using TestClient.Properties;
using System.ServiceModel.Description;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel.Channels;

namespace TestClient
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Fields

        private const string PackageRelationshipType = @"http://schemas.openxmlformats.org/package/2006/relationships/meta data/core-properties";
        private const string ResourceRelationshipType = @"http://schemas.microsoft.com/opc/2006/sample/required-resource";
        private const string ContractsPackageName = "_package.zip";
        private const string ProxiesPackageName = "_proxies.zip";
        private const string UnpackFolderName = "Content";

        #endregion

        #region Constructors

        public MainWindow()
        {
            InitializeComponent();

            this.btnSelectContractsAssemblyFolder.Click += new RoutedEventHandler(this.btnSelectContractsAssemblyFolder_Click);
            this.btnPackAssemblies.Click += new RoutedEventHandler(this.btnPackAssemblies_Click);
            this.btnUnpackAssemblies.Click += new RoutedEventHandler(this.btnUnpackAssemblies_Click);
            this.btnGenerate.Click += new RoutedEventHandler(this.btnGenerate_Click);
            this.txtReceivedPackage.MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(this.txtReceivedPackage_MouseLeftButtonDown);
        }

        #endregion

        #region Methods

        private void PackAssemblies(string folderPath, string destinationPath)
        {
            using (Package package = Package.Open(destinationPath, FileMode.Create))
            {
                DirectoryInfo directoryInfo = new DirectoryInfo(folderPath);
                foreach (FileInfo fileInfo in directoryInfo.EnumerateFiles("*.dll"))
                {
                    string fileName = ".\\" + Path.GetFileName(fileInfo.Name);
                    Uri uri = PackUriHelper.CreatePartUri(new Uri(fileName, UriKind.Relative));
                    PackagePart part = package.CreatePart(uri, "", CompressionOption.Normal);
                    using (FileStream fileStream = fileInfo.OpenRead())
                    {
                        using (Stream partStream = part.GetStream())
                        {
                            fileStream.CopyTo(partStream);
                        }
                    }
                }
            }
        }

        private void UnpackAssemblies(string packagePath, string destinationFolder)
        {
            DirectoryInfo directoryInfo = new DirectoryInfo(destinationFolder);
            if (directoryInfo.Exists)
                directoryInfo.Delete(true);
            directoryInfo.Create();

            using (Package package = Package.Open(packagePath, FileMode.Open, FileAccess.Read))
            {
                foreach (PackagePart part in package.GetParts())
                {
                    string partFileName = part.Uri.ToString().Trim('/');
                    string filePath = Path.Combine(destinationFolder, partFileName);
                    using (Stream partStream = part.GetStream(FileMode.Open, FileAccess.Read))
                    {
                        using (Stream fileStream = File.Open(filePath, FileMode.Create, FileAccess.Write))
                        {
                            partStream.CopyTo(fileStream);
                        }
                    }
                }
            }
        }

        private void ExtractPart(PackagePart packagePart, string destinationFolder)
        {
            // Remove leading slash from the Part Uri, and make a new Uri from the result
            string stringPart = packagePart.Uri.ToString().TrimStart('/');
            Uri partUri = new Uri(stringPart, UriKind.Relative);

            // Create a full Uri to the Part based on the Package Uri
            Uri uriFullPartPath = new Uri(new Uri(destinationFolder, UriKind.Absolute), partUri);

            // Create the necessary Directories based on the Full Part Path
            Directory.CreateDirectory(Path.GetDirectoryName(uriFullPartPath.LocalPath));

            // Create the file with the Part content
            using (FileStream fileStream = new FileStream(uriFullPartPath.LocalPath, FileMode.Create))
            {
                using (Stream partStream = packagePart.GetStream())
                {
                    partStream.CopyTo(fileStream);
                }
            }
        }

        private void GenerateSync()
        {
            this.txtReceivedPackage.Text = null;
            this.txtReceivedPackage.Cursor = System.Windows.Input.Cursors.Arrow;

            if (this.txtContractsAssemblyFolder.Text != null
                && Directory.Exists(this.txtContractsAssemblyFolder.Text))
            {
                string packagePath = System.IO.Path.Combine(this.txtContractsAssemblyFolder.Text, MainWindow.ContractsPackageName);
                if (File.Exists(packagePath))
                {
                    using (FileStream packageStream = File.OpenRead(packagePath))
                    {
                        WCGServiceClient client = this.GetServiceClient();
                        Stream responseStream = client.GenerateProxies(
                            new GenerationArguments
                            {
                                AsyncContractsFileName = "ServiceReferenceContracts.cs",
                                AsyncProxiesFileName = "ServiceReferenceProxies.cs",
                                ClientBaseName = "System.ServiceModel.ClientBase",
                                ServiceContractsNamespace = "http://sample.com/services",
                                SpecificUsings = null,
                                SyncContractsFileName = "SyncServiceReferenceContracts.cs",
                                SyncProxiesFileName = "SyncServiceReferenceProxies.cs",
                                ContractsAssemblyFileName = this.txtContractsAssemblyFileName.Text,
                                AsyncProxiesNeeded = true,
                                SyncProxiesNeeded = true
                            },
                            packageStream
                        );
                        if (responseStream != null)
                        {
                            using (responseStream)
                            {
                                string destinationPath = Path.Combine(this.txtContractsAssemblyFolder.Text, MainWindow.ProxiesPackageName);
                                using (FileStream destinationStream = new FileStream(destinationPath, FileMode.Create))
                                {
                                    responseStream.CopyTo(destinationStream);
                                    this.txtReceivedPackage.Text = destinationPath;
                                    this.txtReceivedPackage.Cursor = System.Windows.Input.Cursors.Hand;
                                }
                            }
                        }
                    }
                }
            }
        }

        private async void GenerateAsync()
        {
            this.txtReceivedPackage.Text = null;
            this.txtReceivedPackage.Cursor = System.Windows.Input.Cursors.Arrow;

            if (!string.IsNullOrEmpty(this.txtContractsAssemblyFolder.Text)
                && !string.IsNullOrEmpty(this.txtContractsAssemblyFileName.Text)
                && Directory.Exists(this.txtContractsAssemblyFolder.Text))
            {
                string packagePath = System.IO.Path.Combine(this.txtContractsAssemblyFolder.Text, MainWindow.ContractsPackageName);
                if (File.Exists(packagePath))
                {
                    using (FileStream packageStream = File.OpenRead(packagePath))
                    {
                        WCGServiceClient client = this.GetServiceClient();
                        Stream responseStream = await Task<Stream>.Factory.FromAsync(
                            client.BeginGenerateProxies,
                            client.EndGenerateProxies,
                            new GenerationArguments
                            {
                                AsyncContractsFileName = "ServiceReferenceContracts.cs",
                                AsyncProxiesFileName = "ServiceReferenceProxies.cs",
                                ClientBaseName = "System.ServiceModel.ClientBase",
                                ServiceContractsNamespace = "http://sample.com/services",
                                SpecificUsings = null,
                                SyncContractsFileName = "SyncServiceReferenceContracts.cs",
                                SyncProxiesFileName = "SyncServiceReferenceProxies.cs",
                                ContractsAssemblyFileName = this.txtContractsAssemblyFileName.Text,
                                AsyncProxiesNeeded = true,
                                SyncProxiesNeeded = true
                            },
                            packageStream,
                            null);

                        if (responseStream != null)
                        {
                            using (responseStream)
                            {
                                string destinationPath = Path.Combine(this.txtContractsAssemblyFolder.Text, MainWindow.ProxiesPackageName);
                                using (FileStream destinationStream = new FileStream(destinationPath, FileMode.Create))
                                {
                                    responseStream.CopyTo(destinationStream);
                                    this.txtReceivedPackage.Text = destinationPath;
                                    this.txtReceivedPackage.Cursor = System.Windows.Input.Cursors.Hand;
                                }
                            }
                        }
                    }
                }
            }
        }

        private void GenerateOldStyle()
        {
            this.txtReceivedPackage.Text = null;
            this.txtReceivedPackage.Cursor = System.Windows.Input.Cursors.Arrow;

            if (!string.IsNullOrEmpty(this.txtContractsAssemblyFolder.Text)
                && !string.IsNullOrEmpty(this.txtContractsAssemblyFileName.Text)
                && Directory.Exists(this.txtContractsAssemblyFolder.Text))
            {
                string packagePath = System.IO.Path.Combine(this.txtContractsAssemblyFolder.Text, MainWindow.ContractsPackageName);
                if (File.Exists(packagePath))
                {
                    FileStream packageStream = File.OpenRead(packagePath);
                    WCGServiceClient client = this.GetServiceClient();
                    client.BeginGenerateProxies(
                        new GenerationArguments
                        {
                            AsyncContractsFileName = "ServiceReferenceContracts.cs",
                            AsyncProxiesFileName = "ServiceReferenceProxies.cs",
                            ClientBaseName = "System.ServiceModel.ClientBase",
                            ServiceContractsNamespace = "http://sample.com/services",
                            SpecificUsings = null,
                            SyncContractsFileName = "SyncServiceReferenceContracts.cs",
                            SyncProxiesFileName = "SyncServiceReferenceProxies.cs",
                            ContractsAssemblyFileName = this.txtContractsAssemblyFileName.Text,
                            AsyncProxiesNeeded = true,
                            SyncProxiesNeeded = true
                        },
                        packageStream,
                        (asyncResult) =>
                        {
                            this.Dispatcher.BeginInvoke(
                                new Action(() =>
                                {
                                    Stream inputStream = asyncResult.AsyncState as Stream;
                                    if (inputStream != null)
                                        inputStream.Dispose();
                                    Stream responseStream = client.EndGenerateProxies(asyncResult);
                                    if (responseStream != null)
                                    {
                                        using (responseStream)
                                        {
                                            string destinationPath = Path.Combine(this.txtContractsAssemblyFolder.Text, MainWindow.ProxiesPackageName);
                                            using (FileStream destinationStream = new FileStream(destinationPath, FileMode.Create))
                                            {
                                                responseStream.CopyTo(destinationStream);
                                                this.txtReceivedPackage.Text = destinationPath;
                                                this.txtReceivedPackage.Cursor = System.Windows.Input.Cursors.Hand;
                                            }
                                        }
                                    }
                                }),
                                null);
                        },
                        packageStream
                    );
                }
            }
        }

        private WCGServiceClient GetServiceClient()
        {
            WCGServiceClient client = null;
            if (Settings.Default.UseSettings)
            {
                //WSHttpBinding binding = new WSHttpBinding();
                //binding.MaxReceivedMessageSize = 67108864;
                //binding.MessageEncoding = WSMessageEncoding.Mtom;
                //binding.MaxBufferPoolSize = 524288;
                //binding.Security.Mode = SecurityMode.Message;
                //binding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;

                //EndpointAddress address = new EndpointAddress(
                //    new Uri(Settings.Default.ServiceUrl),
                //    new DnsEndpointIdentity(Settings.Default.ServerCN),
                //    (AddressHeaderCollection)null);

                //client = new WCGServiceClient(binding, address);

                //client.ClientCredentials.ClientCertificate.SetCertificate(
                //    StoreLocation.LocalMachine,
                //    StoreName.My,
                //    X509FindType.FindBySubjectName,
                //    Settings.Default.ClientCN);
                //if (!Settings.Default.CheckRevocation)
                //    client.ClientCredentials.ServiceCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck;

                NetTcpBinding binding = new NetTcpBinding();
                binding.TransferMode = TransferMode.Streamed;
                binding.MaxReceivedMessageSize = 67108864;

                EndpointAddress address = new EndpointAddress(
                    new Uri(Settings.Default.ServiceUrl));

                client = new WCGServiceClient(binding, address);
            }
            else
            {
                client = new WCGServiceClient(Settings.Default.ServiceEndpoint);
            }
            return client;
        }

        #endregion

        #region Event handlers

        private void btnSelectContractsAssemblyFolder_Click(object sender, RoutedEventArgs e)
        {
            FolderBrowserDialog dialog = new FolderBrowserDialog();
            System.Windows.Forms.DialogResult result = dialog.ShowDialog();
            if (result == System.Windows.Forms.DialogResult.OK)
            {
                this.txtContractsAssemblyFolder.Text = dialog.SelectedPath;
            }
        }

        private void btnPackAssemblies_Click(object sender, RoutedEventArgs e)
        {
            if (this.txtContractsAssemblyFolder.Text != null
                && Directory.Exists(this.txtContractsAssemblyFolder.Text))
            {
                string destinationPath = System.IO.Path.Combine(this.txtContractsAssemblyFolder.Text, MainWindow.ContractsPackageName);
                this.PackAssemblies(this.txtContractsAssemblyFolder.Text, destinationPath);
            }
        }


        private void btnUnpackAssemblies_Click(object sender, RoutedEventArgs e)
        {
            if (this.txtContractsAssemblyFolder.Text != null
                && Directory.Exists(this.txtContractsAssemblyFolder.Text))
            {
                string packagePath = System.IO.Path.Combine(this.txtContractsAssemblyFolder.Text, MainWindow.ContractsPackageName);
                if (File.Exists(packagePath))
                {
                    string destinationFolder = Path.Combine(Path.GetDirectoryName(packagePath), MainWindow.UnpackFolderName);
                    this.UnpackAssemblies(packagePath, destinationFolder);
                }
            }
        }

        private void btnGenerate_Click(object sender, RoutedEventArgs e)
        {
            this.GenerateOldStyle();
        }

        private void txtReceivedPackage_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (!string.IsNullOrEmpty(this.txtReceivedPackage.Text))
                Process.Start(this.txtReceivedPackage.Text);
        }

        #endregion
    }
}
